 /*
    *  Copyright 2004 Thevert Liu  <thevert.liu@gmail.com>
    *
    *  Licensed under the Apache License, Version 2.0 (the "License");
    *  you may not use this file except in compliance with the License.
    *  You may obtain a copy of the License at
    *
    *      http://www.apache.org/licenses/LICENSE-2.0
    *
   *  Unless required by applicable law or agreed to in writing, software
  *  distributed under the License is distributed on an "AS IS" BASIS,
   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   *  See the License for the specific language governing permissions and
   *  limitations under the License.
   */
#include "XMLReaderParser.h"
#include "X2Log.h
Reader::Reader(IXMLReader& reader, IXMLShell& shell) 
  : mReader(reader), mShell(shell)
{
}


bool Reader::marshall() 
{
    std::string pname ;
    mReader.getParentName(pname) ;
    
    const int depth = mReader.getTreeStartDepth();
    x2debug << "Current depth:" << depth  ;
    
    if (!handleAttributes()) {
        x2crit << "handle attribute failed (element:" << pname << ")" << ENDL ;
        return false ;
    }
    
    if (!handleExtra()) {
        x2crit << "handle extra failed (element:" << pname << ")" << ENDL ;
        return false ;
    }
    
    if (!handleElements(depth)) {
        x2crit << "handle elements failed (element:" << pname << ")" << ENDL ;
        return false ;
    }

    return mShell.isWell() ;    
}


int Reader::handleAttribute(IXMLShell::AttributeNode& node)
{
    std::string val ;
    bool ret ;
    if (node.prefix.empty()) {
        ret = mReader.getAttribute(node.name, val) ;
    } else {
        ret = mReader.getAttributeNS(node.prefix, node.name, val) ;
    }
    
    if (ret) { 
        node.adapter->set(val) ;
    } else if (node.adapter->required()){
        x2crit << "handle attribute failed. A required attr not find:" << node.name << ENDL ;
        return 1 ;
    }
    
    return 0 ;
}

bool Reader::handleAttributes() 
{
    ParamHandler<Reader,IXMLShell::AttributeNode&> handler(this, &Reader::handleAttribute) ; 
    return mShell.browse(&handler) ;
}

bool Reader::handleExtra() 
{
    IExtraBlock* block = mShell.getExtraBlock() ;
    if (block==NULL) {
        return true ;
    }
    
    std::string data ;
    if (!mReader.getExpandData(data)) {
        x2crit << "get expand data failed!" << ENDL ;
        return false ;
    }
    
    block->setData(data) ;
    
    return true ;
}

bool Reader::handleElement(const std::string& tagName) 
{
    x2debug << "handle element:" << tagName << ENDL ;
    std::string tag = tagName ; // The tagname maybe changed.
    IShellElement* se = mShell.getElement(tagName) ;
    if (!se && tagName.find(":")!=std::string::npos) {
        std::string ns, name ;
        mReader.getNSName(ns, name) ;
        x2debug << "Get namespace, ns:" << ns << "  localname:" << name << ENDL ;
        /// if user regist a namespace for this element.
        se = mShell.getElementNS(ns, name) ;
        
        if (!se&&mReader.getXMLContext()!=NULL) {
            x2debug << "try to use another prefix to get the shell" << ENDL ;
            std::string px ;
            /// if the user use another prefix to regist the the tagname.
            if (mReader.getXMLContext()->getPrefix(ns, px)) {
                tag = px+":"+name ;
                se = mShell.getElement(tag) ;
                x2debug << "The orgin prefix:" << px << ENDL ;
            }
        }
    }
    
    if (!se) {
        x2crit << "Not find the shell element of :" << tagName << ENDL ;
        return false ;
    }
    
    XMLShell shell ;
    if (!se->create(tag, shell)) {
        x2crit << "Create element faile:" << tag << ENDL ;
        return false ;
    }
    
    Reader reader(mReader, shell) ;
    
    return reader.marshall() ;
    
}

bool Reader::handleValue() 
{
    std::string s ;
    if (!mReader.getValue(s)) {
        x2crit << "reader get value failed!" << ENDL ;
        return false ;
    }
    
    x2debug << "Read value:" << s << ENDL ;
    
    IValue * val ;
    
    if ( (val=mShell.getValue())!=NULL ) {
        val->setValue(s) ;
    } else {
        x2warning << "Value not registed, but find a value." << ENDL ;
    }
    
    
    return true ;
}

bool Reader::handleElements(int depth) 
{
    std::string tagName ;
    bool ret = false ;
    
    while(ret=mReader.nextElement(tagName)) {
        x2debug << "Next Element:" << tagName << ENDL ;
        
        if (mReader.getTreeStartDepth() == depth) {
            x2debug << "Out of depth find:" << mReader.getTreeStartDepth() 
                    << " Current is:" << depth << ENDL ;
            break ;
        }
        if (mReader.isValue()) {
            x2debug << "The element is a value." << ENDL ;
            if (!handleValue()) {
                x2crit << "handle value failed!" << ENDL ;
                return false ;
            }
            continue ;
        }
        if (mReader.isElement()) {
            x2debug << "Is a element." << ENDL ;
            if (!handleElement(tagName)) {
                x2crit << "handle element failed:" << tagName << ENDL ;
                return false ;
            }
        }
    }
    
    return true ;
}


Writer::Writer(IXMLElement& e, std::ostream& os)
   : mElement(e), mOS(os)
{
}

bool Writer::write()
{
    mElement.regist(mShell) ;
    mOS << "<" << mElement.getTagName()  ;

    handleAttributes() ;

    if (mShell.isEmpty()) {
        mOS << "/>" ;
        return true ;
    } else {
        mOS << ">" ;
    }
    
    handleExtra() ;
    handleValue() ;
    handleElements() ;
    
    mOS << "</" << mElement.getTagName() << ">"  ;
    
    return true ;
}

bool Writer::handleValue()
{
    std::string s ;
    IValue *v ;
    if ( (v=mShell.getValue()) != NULL) {
        if (v->getValue(s)) {
            mOS << s ;
        }
    }
}

bool Writer::handleExtra()
{
    std::string s ;
    IExtraBlock *eb ;
    if ( (eb=mShell.getExtraBlock())!=NULL ) {
        if (eb->getData(s)) {
            mOS << s ;
        }
    }
}

int Writer::handleAttribute(IXMLShell::AttributeNode& node) 
{
   std::string val ;
   if (!node.adapter->get(val)) {
       x2warning << "Get attribute value failed:" << node.name << ENDL ;
   }
   
   if (val.empty()) {
       x2warning << "Get attribute value is empty:" << node.name << ENDL ;
   }
   
   mOS << " " << node.name << "=\"" << val << "\"" ;
   
   return 0 ;
}

bool Writer::handleAttributes() 
{
    ParamHandler<Writer, IXMLShell::AttributeNode&> handler(this, &Writer::handleAttribute) ;
    mShell.browse(&handler) ;
    return true ;
}


int Writer::handleXMLElement(IXMLElement* elem)
{
    if (elem==NULL) {
        return 1 ;
    }
    
    Writer writer(*elem, mOS) ;
    writer.write() ;
    return 0 ;
}

int Writer::handleElement(IXMLShell::ElementNode& node)
{
    if (node.adapter->empty()) {
//        x2debug << "Elements is empty:" << node.name << ENDL ;
        return 1;
    }
    
    ParamHandler<Writer, IXMLElement*> handler(this, &Writer::handleXMLElement) ;
    node.adapter->access(&handler) ;
    
    return 0 ;
}

bool Writer::handleElements() 
{
    ParamHandler<Writer, IXMLShell::ElementNode&> handler(this, &Writer::handleElement) ;
    this->mShell.browse(&handler) ;
    return true ;
}


ICreator<IXMLReader>::CreateFunc XMLReaderParser::mCreate = NULL ;

bool XMLReaderParser::parse
  (
     const std::string & xmlbody, 
     const std::string& name, 
     IXMLElement& result, 
     std::string& error, 
     XMLContext* ctx
  )

{
    IXMLReader * reader = mCreate() ;
    if (!reader) {
        return false ;
    }
    
    if (!reader->create(xmlbody, name)) {
        delete reader ;
        return false ;
    }
    
    reader->setContext(ctx) ;
    
    XMLShell shell ;
    result.regist(shell) ;
    
    Reader parser(*reader, shell) ;
    
    if (!parser.marshall()) {
        error = reader->getError() ;
        delete reader ;
        return false ;
    }
    
    delete reader ;
    
    return true ;
} 


bool XMLReaderParser::toXML(IXMLElement& root, std::ostream& os, XMLContext* ctx) 
{
    os << "<?xml version=\"1.0\"" ;

    if (ctx && !ctx->empty()) {
        ctx->write(os) ;
    }

    os << "?>" << std::endl;
    Writer writer(root, os) ;
    return writer.write() ;
}


